import { PropertyAttribute, PropertyMethodAttribute, MethodAttribute } from "./System";
import { IsNull, IsNullOrWhiteSpace } from "../Common/GlobalExtend";

console.log("ty:classruntime...");
export const TYPropPrefix = "TY__";

/**
 * 类库内部使用，不建议外部使用
 *
 * @export
 * @class _KeyValue
 * @template TKey
 * @template TValue
 */
export class _KeyValue<TKey, TValue>{
    private m_Key: TKey;
    private m_Value: TValue;

    public get Key(): TKey {
        return this.m_Key;
    }

    public get Value(): TValue {
        return this.m_Value;
    }

    constructor(key: TKey, value: TValue) {
        this.m_Key = key;
        this.m_Value = value;
    }
}

/**
 * 类库内部使用，不建议外部使用
 *
 * @export
 * @class _KeyValueArray
 * @template TKey
 * @template TValue
 */
export class _KeyValueArray<TKey, TValue> {
    private m_Keys: Array<TKey> = new Array<TKey>();
    private m_Values: Array<TValue> = new Array<TValue>();

    public get Keys(): Array<TKey> {
        return this.m_Keys;
    }

    public get Values(): Array<TValue> {
        return this.m_Values;
    }

    public ContainsKey(key: TKey): boolean {
        return this.m_Keys.Contains(key);
    }
    public Add(Key: TKey, Value: TValue): void {
        if (!this.ContainsKey(Key)) {
            this.m_Keys.push(Key);
            this.m_Values.push(Value);
        }
    }
    public GetValue(Key: TKey): TValue {
        if (this.m_Keys.Contains(Key))
            return this.m_Values[this.m_Keys.indexOf(Key)];
        else
            return null;
    }
    public SetValue(Key: TKey, Value: TValue): void {
        if (this.m_Keys.Contains(Key))
            this.m_Values[this.m_Keys.indexOf(Key)] = Value;
        else
            this.Add(Key, Value);
    }
}

/**
 * 元数据属性接口
 *
 * @export
 * @interface IAttribute
 */
export interface IAttribute { }

/**
 * 类型系统(运行时)
 *
 * @export
 * @class ClassRuntimeInfo
 */
export class ClassRuntimeInfo {
    private m_Name: string;
    private m_FullName: string;
    private m_ParentFullName: string;

    public get FullName(): string {
        return this.m_FullName;
    }
    public get Name(): string {
        return this.m_Name;
    }
    public get Type(): new () => any {
        return ClassInfoDefines.GetClassInfo(this.FullName).Constructor;
    }
    public get ParentClassInfo(): ClassRuntimeInfo {
        if (IsNullOrWhiteSpace(this.m_ParentFullName))
            return null;
        else
            return ClassInfoDefines.GetClassInfo(this.m_ParentFullName).Runtime;
    }

    constructor(_Name: string, _FullName: string, _ParentFullName: string) {
        this.m_Name = _Name;
        this.m_FullName = _FullName;
        this.m_ParentFullName = _ParentFullName;
    }

    /**
     * 获取属性元数据
     *
     * @param {string} PropertyName
     * @param {boolean} [searchInherit=true]
     * @returns {Array<PropertyAttribute>}
     * @memberof ClassRuntimeInfo
     */
    public GetPropertyMetaDatas(PropertyName: string, searchInherit: boolean = true): Array<PropertyAttribute> {
        return ClassInfoDefines.GetPropertyMetaDatas(this.FullName, PropertyName, searchInherit);
    }
    /**
     * 获取属性元数据
     *
     * @template T
     * @param {new (_PropertyName: string) => T} type
     * @param {boolean} [searchInherit=true]
     * @returns {_KeyValueArray<string, Array<T>>}
     * @memberof ClassRuntimeInfo
     */
    public GetPropertyMetaDatasByType<T extends PropertyAttribute>(type: new (_PropertyName: string) => T, searchInherit: boolean = true): _KeyValueArray<string, Array<T>> {
        return ClassInfoDefines.GetPropertyMetaDatasByType(this.FullName, type, searchInherit);
    }
    /**
     * 获取属性元数据
     *
     * @template T
     * @param {new (_PropertyName: string) => T} type
     * @param {string} PropertyName
     * @param {boolean} [searchInherit=true]
     * @returns {boolean}
     * @memberof ClassRuntimeInfo
     */
    public GetPropertyMetaDatasByTypePropertyName<T extends PropertyAttribute>(type: new (_PropertyName: string) => T, PropertyName: string, searchInherit: boolean = true): boolean {
        let r = ClassInfoDefines.GetPropertyMetaDatasByType(this.FullName, type, searchInherit);
        return r.ContainsKey(PropertyName);
    }
    /**
     * 获取属性元数据
     *
     * @param {string} PropertyName
     * @param {boolean} [searchInherit=true]
     * @returns {Array<PropertyMethodAttribute>}
     * @memberof ClassRuntimeInfo
     */
    public GetPropertyMethodMetaDatas(PropertyName: string, searchInherit: boolean = true): Array<PropertyMethodAttribute> {
        return ClassInfoDefines.GetPropertyMethodMetaDatas(this.FullName, PropertyName, searchInherit);
    }
    /**
     * 获取属性元数据
     *
     * @template T
     * @param {new (_PropertyName: string, _Descriptor: PropertyDescriptor) => T} type
     * @param {boolean} [searchInherit=true]
     * @returns {_KeyValueArray<string, Array<T>>}
     * @memberof ClassRuntimeInfo
     */
    public GetPropertyMethodMetaDatasByType<T extends PropertyMethodAttribute>(type: new (_PropertyName: string, _Descriptor: PropertyDescriptor) => T, searchInherit: boolean = true): _KeyValueArray<string, Array<T>> {
        return ClassInfoDefines.GetPropertyMethodMetaDatasByType(this.FullName, type, searchInherit);
    }
    /**
     * 获取属性元数据
     *
     * @template T
     * @param {new (_PropertyName: string, _Descriptor: PropertyDescriptor) => T} type
     * @param {string} PropertyName
     * @param {boolean} [searchInherit=true]
     * @returns {boolean}
     * @memberof ClassRuntimeInfo
     */
    public GetPropertyMethodMetaDatasByTypePropertyName<T extends PropertyMethodAttribute>(type: new (_PropertyName: string, _Descriptor: PropertyDescriptor) => T, PropertyName: string, searchInherit: boolean = true): boolean {
        let r = ClassInfoDefines.GetPropertyMethodMetaDatasByType(this.FullName, type, searchInherit);
        return r.ContainsKey(PropertyName);
    }
    /**
     * 获取方法元数据
     *
     * @param {string} PropertyName
     * @param {boolean} [searchInherit=true]
     * @returns {Array<MethodAttribute>}
     * @memberof ClassRuntimeInfo
     */
    public GetMethodMetaDatas(PropertyName: string, searchInherit: boolean = true): Array<MethodAttribute> {
        return ClassInfoDefines.GetMethodMetaDatas(this.FullName, PropertyName, searchInherit);
    }
    /**
     * 获取方法元数据
     *
     * @template T
     * @param {new (_MethodName: string, _Descriptor: PropertyDescriptor) => T} type
     * @param {boolean} [searchInherit=true]
     * @returns {_KeyValueArray<string, Array<T>>}
     * @memberof ClassRuntimeInfo
     */
    public GetMethodMetaDatasByType<T extends MethodAttribute>(type: new (_MethodName: string, _Descriptor: PropertyDescriptor) => T, searchInherit: boolean = true): _KeyValueArray<string, Array<T>> {
        return ClassInfoDefines.GetMethodMetaDatasByType(this.FullName, type, searchInherit);
    }
    /**
     * 获取方法元数据
     *
     * @template T
     * @param {new (_MethodName: string, _Descriptor: PropertyDescriptor) => T} type
     * @param {string} PropertyName
     * @param {boolean} [searchInherit=true]
     * @returns {boolean}
     * @memberof ClassRuntimeInfo
     */
    public GetMethodMetaDatasByTypePropertyName<T extends MethodAttribute>(type: new (_MethodName: string, _Descriptor: PropertyDescriptor) => T, PropertyName: string, searchInherit: boolean = true): boolean {
        let r = ClassInfoDefines.GetMethodMetaDatasByType(this.FullName, type, searchInherit);
        return r.ContainsKey(PropertyName);
    }

    private TY__IsSubClassOf(runtime: ClassRuntimeInfo, parentFullClassName: string): boolean {
        if (IsNull(parentFullClassName))
            return false;
        if (IsNull(runtime.ParentClassInfo))
            return false;
        if (runtime.ParentClassInfo.FullName == parentFullClassName)
            return true;
        else
            return this.TY__IsSubClassOf(runtime.ParentClassInfo, parentFullClassName);
    }

    /**
     * 检查类型集成关系
     *
     * @param {new () => any} type
     * @returns {boolean}
     * @memberof ClassRuntimeInfo
     */
    public IsSubClassOf(type: new () => any): boolean {
        return this.TY__IsSubClassOf(this, (type as any).TY__FullName);
    }
}

export class ClassInfoDefines {
    private static m_AllClassRuntimes: _KeyValueArray<string, ClassRuntimeInfo> = new _KeyValueArray<string, ClassRuntimeInfo>();
    private static m_AllClassInfos: _KeyValueArray<string, new () => any> = new _KeyValueArray<string, new () => any>();

    private static m_AllPropertyMetaDatas: _KeyValueArray<string, _KeyValueArray<string, Array<PropertyAttribute>>> = new _KeyValueArray<string, _KeyValueArray<string, Array<PropertyAttribute>>>();
    private static m_AllPropertyMethodMetaDatas: _KeyValueArray<string, _KeyValueArray<string, Array<PropertyMethodAttribute>>> = new _KeyValueArray<string, _KeyValueArray<string, Array<PropertyMethodAttribute>>>();
    private static m_AllMethodMetaDatas: _KeyValueArray<string, _KeyValueArray<string, Array<MethodAttribute>>> = new _KeyValueArray<string, _KeyValueArray<string, Array<MethodAttribute>>>();

    /**
     * 检查是否有该类的类型系统定义
     *
     * @static
     * @param {string} className
     * @returns {boolean}
     * @memberof ClassInfoDefines
     */
    public static ContainsClass(className: string): boolean {
        return ClassInfoDefines.m_AllClassRuntimes.ContainsKey(className);
    }
    /**
     * 增加类型定义系统
     *
     * @static
     * @param {string} className
     * @param {ClassRuntimeInfo} runtime
     * @param {new () => any} constructor
     * @memberof ClassInfoDefines
     */
    public static AddClassInfo(className: string, runtime: ClassRuntimeInfo, constructor: new () => any): void {
        if (!ClassInfoDefines.m_AllClassRuntimes.ContainsKey(className)) {
            ClassInfoDefines.m_AllClassRuntimes.Add(className, runtime);
            ClassInfoDefines.m_AllClassInfos.Add(className, constructor);
        }
        else {
            if (console != undefined)
                console.log(`ClassInfo：${className}重复定义!`);
            else
                throw `ClassInfo：${className}重复定义!`;
        }
    }
    /**
     * 根据类名获取类型系统
     *
     * @static
     * @param {string} className
     * @returns {{ Name: string, Runtime: ClassRuntimeInfo, Constructor: new () => any }}
     * @memberof ClassInfoDefines
     */
    public static GetClassInfo(className: string): { Name: string, Runtime: ClassRuntimeInfo, Constructor: new () => any } {
        if (ClassInfoDefines.m_AllClassRuntimes.ContainsKey(className)) {
            return {
                Name: className,
                Runtime: ClassInfoDefines.m_AllClassRuntimes.GetValue(className),
                Constructor: ClassInfoDefines.m_AllClassInfos.GetValue(className)
            };
        }
        else
            return null;
    }
    /**
     * 增加属性元数据
     * 
     * @static
     * @param {string} className
     * @param {_KeyValueArray<string, Array<PropertyAttribute>>} metas
     * @memberof ClassInfoDefines
     */
    public static AddPropertyMetaDatas(className: string, metas: _KeyValueArray<string, Array<PropertyAttribute>>): void {
        if (!ClassInfoDefines.m_AllPropertyMetaDatas.ContainsKey(className))
            ClassInfoDefines.m_AllPropertyMetaDatas.Add(className, metas);
    }
    /**
     * 获取属性元数据
     *
     * @static
     * @param {string} className
     * @param {string} PropertyName
     * @param {boolean} [searchInherit=true]
     * @returns {Array<PropertyAttribute>}
     * @memberof ClassInfoDefines
     */
    public static GetPropertyMetaDatas(className: string, PropertyName: string, searchInherit: boolean = true): Array<PropertyAttribute> {
        let result: Array<PropertyAttribute> = new Array<PropertyAttribute>();
        if (searchInherit) {
            let cr = this.GetClassInfo(className).Runtime;
            if (cr.ParentClassInfo != null) {
                let _metas = this.GetPropertyMetaDatas(cr.ParentClassInfo.FullName, PropertyName, searchInherit);
                _metas.ForEach((item, index) => result.push(item));
            }
        }

        if (ClassInfoDefines.m_AllPropertyMetaDatas.ContainsKey(className)) {
            let metas = ClassInfoDefines.m_AllPropertyMetaDatas.GetValue(className);
            if (metas.ContainsKey(PropertyName)) {
                let _metas = metas.GetValue(PropertyName);
                _metas.ForEach((item, index) => result.push(item));
            }
        }
        return result;
    }
    /**
     * 获取属性元数据
     *
     * @static
     * @template T
     * @param {string} className
     * @param {new (_PropertyName: string) => T} type
     * @param {boolean} [searchInherit=true]
     * @returns {_KeyValueArray<string, Array<T>>}
     * @memberof ClassInfoDefines
     */
    public static GetPropertyMetaDatasByType<T extends PropertyAttribute>(className: string, type: new (_PropertyName: string) => T, searchInherit: boolean = true): _KeyValueArray<string, Array<T>> {
        let result: _KeyValueArray<string, Array<T>> = new _KeyValueArray<string, Array<T>>();

        if (searchInherit) {
            let cr = this.GetClassInfo(className).Runtime;
            if (cr.ParentClassInfo != null) {
                let _r = this.GetPropertyMetaDatasByType(cr.ParentClassInfo.FullName, type, searchInherit);
                let i = 0;
                for (i = 0; i < _r.Keys.length; i++)
                    result.SetValue(_r.Keys[i], _r.Values[i]);
            }
        }

        if (ClassInfoDefines.m_AllPropertyMetaDatas.ContainsKey(className)) {
            let metas = ClassInfoDefines.m_AllPropertyMetaDatas.GetValue(className);
            let i = 0;
            for (i = 0; i < metas.Keys.length; i++) {
                let pName = metas.Keys[i];
                let attrs = metas.GetValue(pName);
                let j = 0;
                let arrs = new Array<T>();
                for (j = 0; j < attrs.length; j++) {
                    if (attrs[j] instanceof type)
                        arrs.push(attrs[j] as T);
                }
                if (attrs.length > 0)
                    result.SetValue(pName, arrs);
            }
        }
        return result;
    }
    /**
     * 增加属性元数据
     *
     * @static
     * @param {string} className
     * @param {_KeyValueArray<string, Array<PropertyMethodAttribute>>} metas
     * @memberof ClassInfoDefines
     */
    public static AddPropertyMethodMetaDatas(className: string, metas: _KeyValueArray<string, Array<PropertyMethodAttribute>>): void {
        if (!ClassInfoDefines.m_AllPropertyMethodMetaDatas.ContainsKey(className))
            ClassInfoDefines.m_AllPropertyMethodMetaDatas.Add(className, metas);
    }
    /**
     * 获取属性元数据
     *
     * @static
     * @param {string} className
     * @param {string} PropertyName
     * @param {boolean} [searchInherit=true]
     * @returns {Array<PropertyMethodAttribute>}
     * @memberof ClassInfoDefines
     */
    public static GetPropertyMethodMetaDatas(className: string, PropertyName: string, searchInherit: boolean = true): Array<PropertyMethodAttribute> {
        let result: Array<PropertyMethodAttribute> = new Array<PropertyMethodAttribute>();
        if (searchInherit) {
            let cr = this.GetClassInfo(className).Runtime;
            if (cr.ParentClassInfo != null) {
                let _metas = this.GetPropertyMethodMetaDatas(cr.ParentClassInfo.FullName, PropertyName, searchInherit);
                _metas.ForEach((item, index) => result.push(item));
            }
        }

        if (ClassInfoDefines.m_AllPropertyMethodMetaDatas.ContainsKey(className)) {
            let metas = ClassInfoDefines.m_AllPropertyMethodMetaDatas.GetValue(className);
            if (metas.ContainsKey(PropertyName)) {
                let _metas = metas.GetValue(PropertyName);
                _metas.ForEach((item, index) => result.push(item));
            }
        }
        return result;
    }
    /**
     * 获取属性元数据
     *
     * @static
     * @template T
     * @param {string} className
     * @param {new (_PropertyName: string, _Descriptor: PropertyDescriptor) => T} type
     * @param {boolean} [searchInherit=true]
     * @returns {_KeyValueArray<string, Array<T>>}
     * @memberof ClassInfoDefines
     */
    public static GetPropertyMethodMetaDatasByType<T extends PropertyMethodAttribute>(className: string, type: new (_PropertyName: string, _Descriptor: PropertyDescriptor) => T, searchInherit: boolean = true): _KeyValueArray<string, Array<T>> {
        let result: _KeyValueArray<string, Array<T>> = new _KeyValueArray<string, Array<T>>();

        if (searchInherit) {
            let cr = this.GetClassInfo(className).Runtime;
            if (cr.ParentClassInfo != null) {
                let _r = this.GetPropertyMethodMetaDatasByType(cr.ParentClassInfo.FullName, type, searchInherit);
                let i = 0;
                for (i = 0; i < _r.Keys.length; i++)
                    result.SetValue(_r.Keys[i], _r.Values[i]);
            }
        }

        if (ClassInfoDefines.m_AllPropertyMethodMetaDatas.ContainsKey(className)) {
            let metas = ClassInfoDefines.m_AllPropertyMethodMetaDatas.GetValue(className);
            let i = 0;
            for (i = 0; i < metas.Keys.length; i++) {
                let pName = metas.Keys[i];
                let attrs = metas.GetValue(pName);
                let j = 0;
                let arrs = new Array<T>();
                for (j = 0; j < attrs.length; j++) {
                    if (attrs[j] instanceof type)
                        arrs.push(attrs[j] as T);
                }
                if (attrs.length > 0)
                    result.SetValue(pName, arrs);
            }
        }
        return result;
    }
    /**
     * 增加方法元数据
     *
     * @static
     * @param {string} className
     * @param {_KeyValueArray<string, Array<MethodAttribute>>} metas
     * @memberof ClassInfoDefines
     */
    public static AddMethodMetaDatas(className: string, metas: _KeyValueArray<string, Array<MethodAttribute>>): void {
        if (!ClassInfoDefines.m_AllMethodMetaDatas.ContainsKey(className))
            ClassInfoDefines.m_AllMethodMetaDatas.Add(className, metas);
    }
    /**
     * 获取方法元数据
     *
     * @static
     * @param {string} className
     * @param {string} PropertyName
     * @param {boolean} [searchInherit=true]
     * @returns {Array<MethodAttribute>}
     * @memberof ClassInfoDefines
     */
    public static GetMethodMetaDatas(className: string, PropertyName: string, searchInherit: boolean = true): Array<MethodAttribute> {
        let result: Array<MethodAttribute> = new Array<MethodAttribute>();
        if (searchInherit) {
            let cr = this.GetClassInfo(className).Runtime;
            if (cr.ParentClassInfo != null) {
                let _metas = this.GetMethodMetaDatas(cr.ParentClassInfo.FullName, PropertyName, searchInherit);
                _metas.ForEach((item, index) => result.push(item));
            }
        }

        if (ClassInfoDefines.m_AllMethodMetaDatas.ContainsKey(className)) {
            let metas = ClassInfoDefines.m_AllMethodMetaDatas.GetValue(className);
            if (metas.ContainsKey(PropertyName)) {
                let _metas = metas.GetValue(PropertyName);
                _metas.ForEach((item, index) => result.push(item));
            }
        }
        return result;
    }
    /**
     * 获取方法元数据
     *
     * @static
     * @template T
     * @param {string} className
     * @param {new (_MethodName: string, _Descriptor: PropertyDescriptor) => T} type
     * @param {boolean} [searchInherit=true]
     * @returns {_KeyValueArray<string, Array<T>>}
     * @memberof ClassInfoDefines
     */
    public static GetMethodMetaDatasByType<T extends MethodAttribute>(className: string, type: new (_MethodName: string, _Descriptor: PropertyDescriptor) => T, searchInherit: boolean = true): _KeyValueArray<string, Array<T>> {
        let result: _KeyValueArray<string, Array<T>> = new _KeyValueArray<string, Array<T>>();

        if (searchInherit) {
            let cr = this.GetClassInfo(className).Runtime;
            if (cr.ParentClassInfo != null) {
                let _r = this.GetMethodMetaDatasByType(cr.ParentClassInfo.FullName, type, searchInherit);
                let i = 0;
                for (i = 0; i < _r.Keys.length; i++)
                    result.SetValue(_r.Keys[i], _r.Values[i]);
            }
        }

        if (ClassInfoDefines.m_AllMethodMetaDatas.ContainsKey(className)) {
            let metas = ClassInfoDefines.m_AllMethodMetaDatas.GetValue(className);
            let i = 0;
            for (i = 0; i < metas.Keys.length; i++) {
                let pName = metas.Keys[i];
                let attrs = metas.GetValue(pName);
                let j = 0;
                let arrs = new Array<T>();
                for (j = 0; j < attrs.length; j++) {
                    if (attrs[j] instanceof type)
                        arrs.push(attrs[j] as T);
                }
                if (attrs.length > 0)
                    result.SetValue(pName, arrs);
            }
        }
        return result;
    }
}

export var ClassRuntimeDecorator = (nameSpace: string): (targetConstructor: any) => void => {
    return (targetConstructor: any): void => {        
        let parentName = null;
        if (targetConstructor.prototype.__proto__ != null && targetConstructor.prototype.__proto__ != undefined)
            parentName = targetConstructor.prototype.__proto__.constructor.TY__FullName;
        if (parentName != null && parentName != undefined) {
            if (!ClassInfoDefines.ContainsClass(parentName)) {
                if (console != undefined)
                    console.log(`ParentClassInfo：${parentName}没有定义!`);
                else
                    throw `ParentClassInfo：${parentName}没有定义!`;
            }
        }
        let targetName = targetConstructor.name;
        if (IsNullOrWhiteSpace(targetName))
            targetName = targetConstructor.toString().substr(9, targetConstructor.toString().indexOf("(") - 9);
        if (IsNullOrWhiteSpace(targetName))
            throw "获取不到类名：" + targetConstructor.toString();

        let r: ClassRuntimeInfo = new ClassRuntimeInfo(targetName, `${nameSpace}.${targetName}`, parentName);
        ClassInfoDefines.AddClassInfo(r.FullName, r, targetConstructor);
        Object.defineProperty(targetConstructor, "TY__FullName", {
            writable: true,
            enumerable: false,
            value: r.FullName
        });
        //console.log("call class...",targetName,parentName,targetConstructor.TY__FullName,targetConstructor.prototype);

        //处理元数据
        if (!IsNull(targetConstructor.TY__PropertyMetaDatas)) {
            let metas: _KeyValueArray<string, Array<PropertyAttribute>> = targetConstructor.TY__PropertyMetaDatas.Props as _KeyValueArray<string, Array<PropertyAttribute>>;
            ClassInfoDefines.AddPropertyMetaDatas(r.FullName, metas);
        }
        if (!IsNull(targetConstructor.TY__PropertyMethodMetaDatas)) {
            let metas: _KeyValueArray<string, Array<PropertyMethodAttribute>> = targetConstructor.TY__PropertyMethodMetaDatas.Props as _KeyValueArray<string, Array<PropertyMethodAttribute>>;
            ClassInfoDefines.AddPropertyMethodMetaDatas(r.FullName, metas);
        }
        if (!IsNull(targetConstructor.TY__MethodMetaDatas)) {
            let metas: _KeyValueArray<string, Array<MethodAttribute>> = targetConstructor.TY__MethodMetaDatas.Props as _KeyValueArray<string, Array<MethodAttribute>>;
            ClassInfoDefines.AddMethodMetaDatas(r.FullName, metas);
        }
    }
}

export var SystemClassRuntimeDecorator = (): (target: any) => void => {
    return ClassRuntimeDecorator("TYLibrary.System");
}